#ifndef _PEER_H_
#define _PEER_H_

#define DELAY 0.0
#define MAX_PIECE_NUM 1000
#define DEFAULT_PLAY_TIME 1
//#define DEFAULT_DOWNLOAD_RATE 1
//#define DEFAULT_UPLOAD_RATE 1
#define DEFAULT_SERVER_CAPACITY 10
//#define DEFAULT_DISK_SPACE 1000
#define PEER_NUM 1000
#define MOVIE_NUM 100
#define JOIN_RATE 100
#define LEAVE_RATE 50
#define FINISH_LEAVE_RATE 0.5
#define START_PIECE_COUNT 10
#define ROUND_COUNT 100000
#define PLAY_RATE 1
#define WINDOW_SIZE 20 

#define debug_print(fp, format, ...) fprintf(fp, format, ## __VA_ARGS__)

FILE * logfile;

struct Movie
{
	Movie() : m_movie_id(0), m_piece_number(MAX_PIECE_NUM), m_play_time(1), last_play_time(0), m_bit_field(bitset<MAX_PIECE_NUM>())
	{}

	Movie(const Movie& m) : m_movie_id(m.m_movie_id), m_piece_number(m.m_piece_number), m_play_time(m.m_play_time), last_play_time(m.last_play_time), m_bit_field(bitset<MAX_PIECE_NUM>())
	{}

	Movie& operator=(const Movie& m)
	{
	  m_movie_id = m.m_movie_id;
	  m_piece_number = m.m_piece_number;
	  m_play_time = m.m_play_time; 
	  last_play_time = m.last_play_time;
	  return *this;
	}

	~Movie() {}

	int m_movie_id;
	
	int m_piece_number; 
	
	int m_play_time; //Second

    int last_play_time;
	
	bitset<MAX_PIECE_NUM> m_bit_field;

    /*bool operator < (Movie& movie)
    {
        return last_play_time < movie.last_play_time;
    }*/
};

struct Peer
{
	Peer():m_alive(true), m_peer_id(0), m_watch_movie(0), m_download_process(0), m_download_num(0), m_disk_usage(0), m_delay_piece(0), m_download_usage(0), m_upload_usage(0), m_movie_list(vector<Movie>())
	{}

	Peer(const Peer& p)
	{
	  m_alive = p.m_alive;
	  m_peer_id = p.m_peer_id;
	  m_watch_movie = p.m_watch_movie;
	  m_download_process = p.m_download_process;
	  m_download_num = p.m_download_num;
	  m_download_rate = p.m_download_rate;
	  m_upload_rate = p.m_upload_rate;
	  m_disk_size = p.m_disk_size;
	  m_disk_usage = p.m_disk_usage;
	  m_delay_piece = p.m_delay_piece;
	  m_download_usage = p.m_download_usage;
	  m_upload_usage = p.m_upload_usage;
	  m_movie_list = p.m_movie_list;
	}

	Peer& operator=(const Peer& p)
	{
	  m_alive = p.m_alive;
	  m_peer_id = p.m_peer_id;
	  m_watch_movie = p.m_watch_movie;
	  m_download_process = p.m_download_process;
	  m_download_num = p.m_download_num;
	  m_download_rate = p.m_download_rate;
	  m_upload_rate = p.m_upload_rate;
	  m_disk_size = p.m_disk_size;
	  m_disk_usage = p.m_disk_usage;
	  m_delay_piece = p.m_delay_piece;
	  m_download_usage = p.m_download_usage;
	  m_upload_usage = p.m_upload_usage;
	  m_movie_list = p.m_movie_list;
	  return *this;
	}
	  
	~Peer(){}


    bool m_alive;
       
	int m_peer_id;

	int m_watch_movie;
	
	int m_watch_process;
	
	int m_download_process; //piece [1, m_download_process]
	
	int m_download_num;

	int m_download_rate;
	
	int m_upload_rate;

	int m_disk_size;
	
	int m_disk_usage;

    int m_delay_piece;
	
	int m_download_usage;
	
	int m_upload_usage;

	vector<Movie> m_movie_list;
	
	int compare(Peer & peer)
	{
        if(m_peer_id > peer.m_peer_id) return 1;
        else if(m_peer_id < peer.m_peer_id) return -1;
        else return 0;  
    }
    
    void prt_state()
    {
        debug_print(logfile, "peer id: %d, watch_movie: %d, watch_process: %d, download_process: %d\n", m_peer_id, m_watch_movie, m_watch_process, m_download_process);         
    }
	
	bool check_has_movie(int index)
	{
        for(vector<Movie>::const_iterator it = m_movie_list.begin(); it != m_movie_list.end(); it++)
            if(it->m_movie_id == index)
                return true;
        return false;     
    }
    
    int watch_movie()
    {
        for(int i = 0; i != m_movie_list.size(); i++)
            if(m_movie_list[i].m_movie_id == m_watch_movie)
                return i;  
    }
};

struct Server
{
	int m_upload_rate;
	
	int m_upload_usage;

	vector<Movie> m_movie_list;
};


class VoDSystem
{
	public:
        void init()
        {
            init_movies();     
            init_peers();
            init_server();
            init_replica();
        }
           
        void init_server();
        
		void init_peers();

		void init_movies();
		
		void init_replica();
		
		void peer_join(int num = 1);

		void peer_leave();
		
		void peer_leave(Peer & peer);
		
		void replica_replace1(Peer& peer);
		
		void replica_replace2(Peer& peer);
		
		void replica_replace3(Peer& peer);
		
		int request_pieces1(Peer& peer);
		
		int request_pieces2(Peer& peer);
		
		int request_pieces3(Peer& peer);
		
		int request_pieces4(Peer& peer);
		
		int request_pieces5(Peer& peer);
		
		int request_peers1(int movie_id, int piece_id);
		
		int request_peers2(int movie_id, int piece_id);
		
		int request_peers3(int movie_id, int piece_id);
		
		void run(int piece_policy, int peer_policy, int replica_policy);
	
	private:
		
		int generate_random(int num)
		{
            		if(num > 0) num = num * m_peers[num-1].m_watch_movie * m_peers[num-1].m_watch_process * m_peers[num-1].m_download_process + num + m_peers[num-1].m_watch_movie + m_peers[num-1].m_watch_process + m_peers[num-1].m_download_process;
			srand(num+time(NULL));
			return rand();
		}
		
		int generate_random()
		{
            		static int last_value = 0;
			srand(last_value + time(NULL));
			last_value = rand();
	                return last_value;
		}

		int generate_watch_movie(int num)
		{
            		if(num > 0) num = num * m_peers[num-1].m_watch_movie * m_peers[num-1].m_watch_process * m_peers[num-1].m_download_process + num + m_peers[num-1].m_watch_movie + m_peers[num-1].m_watch_process + m_peers[num-1].m_download_process;
			srand(num+time(NULL));
			if(rand() % 100 < 80)
				return rand() % 20;
			else
				return rand() % 80 + 20;
		}
		
		vector<Peer> m_peers;
		
		Server server;
		
		vector<Movie> m_movies;
		
		//Replica Statistics
		vector< vector<int> > m_replica_num;
		
		//Watching Statistics
		vector<int> m_watch_num;
};


#endif
